namespace KanbanBoard.Data.NHibernateMaps
{
    using System;
    using System.Linq;
    using System.Reflection;

    using Conventions;

    using Core;

    using FluentNHibernate;
    using FluentNHibernate.Automapping;
    using FluentNHibernate.Conventions;

    using SharpArch.Core.DomainModel;
    using SharpArch.Data.NHibernate.FluentNHibernate;

    public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
    {
        public AutoPersistenceModel Generate()
        {
            var mappings = new AutoPersistenceModel()
                // If you delete the default class, simply point the following line to an entity within the .Core layer
                .AddEntityAssembly(Assembly.GetAssembly(typeof(User)))
                .Where(this.GetAutoMappingFilter).Conventions.Setup(this.GetConventions()).Setup(this.GetSetup()).
                UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();

            return mappings;
        }

        private Action<AutoMappingExpressions> GetSetup()
        {
            return c =>
                {
                    c.FindIdentity = type => type.Name == "Id";
                    c.IsBaseType = this.IsBaseTypeConvention;
                };
        }

        private Action<IConventionFinder> GetConventions()
        {
            return c =>
                {
                    c.Add<PrimaryKeyConvention>();
                    c.Add<ReferenceConvention>();
                    c.Add<HasManyConvention>();
                    c.Add<TableNameConvention>();
                    c.Add<PropertyNameConvention>();
                };
        }

        /// <summary>
        /// Provides a filter for only including types which inherit from the IEntityWithTypedId interface.
        /// </summary>
        private bool GetAutoMappingFilter(Type t)
        {
            return t.GetInterfaces().Any(x =>
                                         x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>));
        }

        private bool IsBaseTypeConvention(Type arg)
        {
            bool derivesFromEntity = arg == typeof(Entity);
            bool derivesFromEntityWithTypedId = arg.IsGenericType &&
                                                (arg.GetGenericTypeDefinition() == typeof(EntityWithTypedId<>));

            return derivesFromEntity || derivesFromEntityWithTypedId;
        }
    }
}